# !/usr/bin/python3
# -*- coding: utf-8 -*-
"""
@Time ： 3/12/2020 17:36
@Auth ： TomWuang
@File ：qixiang4.0.py
@IDE ：PyCharm

"""
import os
import datetime
import json
import re
import time
import requests
from bs4 import BeautifulSoup
from pymongo import MongoClient
import fake_useragent
from apscheduler.schedulers.blocking import BlockingScheduler
from qcloudsms_py import SmsSingleSender
from qcloudsms_py.httpclient import HTTPError


class GetQiXiang:
    def __init__(self):
        self.client = MongoClient('127.0.0.1', 27017)
        self.is_today = True
        self.lose_city = []
        self.shi_kuang_lose_total = 0
        self.yu_bao_lose_total = 0

    @staticmethod
    def log_to_file(message):
        out_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
        date = time.strftime('%Y-%m-%d', time.localtime(time.time()))
        message = "{}=>{}".format(out_time, message)
        file_path = os.path.join(os.getcwd(), "logs")
        if not os.path.exists(file_path):
            os.mkdir(file_path)
        filename = os.path.join(file_path, "{}.log".format(date))
        try:
            with open(filename, "a", encoding="utf-8") as file:
                file.write(message + "\n")
            print(message)
        except Exception as e:
            print("{}=>{}".format(out_time, "错误：{}".format(e)))

    @staticmethod
    def sms(params):
        # 短信应用 SDK AppID
        appid = 1400000000  # SDK AppID 以1400开头
        # 短信应用 SDK AppKey
        appkey = "************************"
        # 需要发送短信的手机号码
        # phone_numbers = ["21212313123", "12345678902", "12345678903"]
        phone_numbers = ["*******************"]
        # 短信模板ID，需要在短信控制台中申请
        template_id = 0000000  # NOTE: 这里的模板 ID`7839`只是示例，真实的模板 ID 需要在短信控制台中申请
        # 签名
        sms_sign = "*******"  # NOTE: 签名参数使用的是`签名内容`，而不是`签名ID`。这里的签名"腾讯云"只是示例，真实的签名需要在短信控制台中申请

        ssender = SmsSingleSender(appid, appkey)
        # params = ["5678"]  # 当模板没有参数时，`params = []`
        try:
            result = ssender.send_with_param(86, phone_numbers[0], template_id, params, sign=sms_sign, extend="", ext="")  # 签名参数未提供或者为空时，会使用默认签名发送短信
            print(result)
        except HTTPError as e:
            print(e)
        except Exception as e:
            print(e)

    @staticmethod
    def get_city():
        with open("./data/CityList.json", 'r', encoding='utf-8') as load_f:
            city_list = json.load(load_f)
            return city_list

    def time_to_int(self, time_):
        if "日" in time_:
            time_ = time_[-5:-1]
            self.is_today = False
        now_ = datetime.datetime.now()
        now_day = now_.strftime("%Y-%m-%d")
        if not self.is_today:
            now_day = (datetime.datetime.strptime(now_day, '%Y-%m-%d') + datetime.timedelta(days=1)).strftime("%Y-%m-%d")
        now_time = "{} {}".format(now_day, time_)
        time_tup = time.strptime(now_time, "%Y-%m-%d %H:%M")
        time_int = int(time.mktime(time_tup))
        return time_int

    @staticmethod
    def get_ua():
        location = os.path.join(os.getcwd(), 'data', 'fake_useragent.json')
        ua = fake_useragent.UserAgent(path=location)
        # ua = fake_useragent.UserAgent()
        return ua.random

    def get_item_yu_bao(self, item_info, db_coll, try_str=None):
        city_name = item_info['city']
        city_code = item_info['code']
        try:
            url = 'http://www.nmc.cn{}'.format(item_info['url'])
            headers = {
                "user-agent": self.get_ua()
            }
            txt = requests.get(url, headers=headers, timeout=5)
            if txt.status_code == 200:
                txt = txt.content.decode('utf-8')
                soup = BeautifulSoup(txt, "lxml")
                soup = soup.select('#day0 .hour3')
                for div in soup:
                    div = str(div).replace("\n", "")
                    div_ = re.search(
                        '<div class=".*?"><div>(.*?)</div><div class=".*?"><img src="(.*?)"/></div><div>(.*?)</div><div class=".*?">(.*?)</div><div>(.*?)</div><div>(.*?)</div><div>(.*?)</div><div>(.*?)</div><div class=".*?">(.*?)</div></div>',
                        div, re.S)
                    time_int = self.time_to_int(div_.group(1).strip())
                    one = {
                        "city": city_name,
                        "code": city_code,
                        "time": time_int,
                        "tqxx": div_.group(2).strip(),
                        "wd": div_.group(4).strip(),
                        "js": div_.group(3).strip(),
                        "winds": div_.group(5).strip(),
                        "windd": div_.group(6).strip(),
                        "qy": div_.group(7).strip(),
                        "xdsd": div_.group(8).strip(),
                        "yl": div_.group(9).strip(),
                        "njd": ""
                    }
                    if not db_coll.find_one({'time': time_int}):
                        db_coll.insert_one(one)
                        self.log_to_file("SUCCESS：【预报】【{}】【{}】存储成功！".format(city_name, time_int))
                    else:
                        self.log_to_file("WARNING：【预报】【{}】【{}】已存在！".format(city_name, time_int))
                self.is_today = True
                if try_str:
                    self.log_to_file("SUCCESS：【预报】重试获取【{}】成功！".format(city_name))
                else:
                    self.log_to_file("SUCCESS：【预报】【{}】完成！".format(city_name))
                return True
            else:
                if try_str:
                    self.log_to_file("ERROR：【预报】第{}次重试获取【{}】错误！原因：状态码错误（{}）！".format(self.yu_bao_lose_total, city_name, txt.status_code))
                else:
                    self.log_to_file("ERROR：【预报】【{}】获取错误！原因：状态码错误（{}）！".format(city_name, txt.status_code))
                return False
        except Exception as e:
            if try_str:
                self.log_to_file("ERROR：【预报】第{}次重试获取【{}】错误！原因：{}".format(self.yu_bao_lose_total, city_name, e))
            else:
                self.log_to_file("ERROR：【预报】【{}】获取错误！原因：{}".format(city_name, e))
            return False

    def _get_yu_bao(self, city_code="", city_name="", item=None, coll=None, try_str=None):
        city_data = self.get_item_yu_bao(item, coll, try_str=try_str)
        if not city_data:
            if self.yu_bao_lose_total >= 3:
                self.log_to_file("ERROR：【预报】重试存储【{}】失败！".format(city_name))
                return False
            self.yu_bao_lose_total += 1
            self.log_to_file("WARNING：【预报】存储【{}】失败！正在第{}次重试……".format(city_name, self.yu_bao_lose_total))
            return self._get_yu_bao(city_code=city_code, city_name=city_name, item=item, coll=coll, try_str=True)

    def get_yu_bao(self):
        db = self.client.yubao
        for item in self.get_city():
            self.yu_bao_lose_total = 0
            city_code = item['code']
            city_name = item['city']
            coll = db[city_code]
            self._get_yu_bao(city_code=city_code, city_name=city_name, item=item, coll=coll, try_str=False)
            time.sleep(2)
        self.client.close()

    def get_item_shi_kuang(self, url, city_name, city_code, my_coll, try_str=None):
        try:
            txt = requests.get(url, timeout=5)
            if txt.status_code == 200:
                txt = txt.text
                data_json = json.loads(txt)
                for x in data_json["data"]["passedchart"]:
                    if not my_coll.find_one({'time': x['time']}):
                        x['city'] = city_name
                        x['code'] = city_code
                        my_coll.insert_one(x)
                        self.log_to_file("SUCCESS：【实况】【{}】【{}】存储成功！".format(city_name, x['time']))
                    else:
                        self.log_to_file("WARNING：【实况】【{}】【{}】已存在！".format(city_name, x['time']))
                if try_str:
                    self.log_to_file("SUCCESS：【实况】重试获取【{}】成功！".format(city_name))
                else:
                    self.log_to_file("SUCCESS：【实况】【{}】完成！".format(city_name))
                return True
            else:
                if try_str:
                    self.log_to_file("ERROR：【实况】第{}次重试获取【{}】错误！原因：状态码错误（{}）！".format(self.shi_kuang_lose_total, city_name, txt.status_code))
                else:
                    self.log_to_file("ERROR：【实况】【{}】状态码错误（{}）！".format(city_name, txt.status_code))
                return False
        except Exception as e:
            if try_str:
                self.log_to_file("ERROR：【实况】第{}次重试获取【{}】错误！原因：{}".format(self.yu_bao_lose_total, city_name, e))
            else:
                self.log_to_file("ERROR：【实况】【{}】获取错误！原因：{}".format(city_name, e))
            return False

    def _get_shi_kuang(self, city_name, city_code, my_coll, try_str=None):
        url = "http://www.nmc.cn/rest/weather?stationid={}&_={}".format(city_code, int(round(time.time() * 1000)))
        city_data = self.get_item_shi_kuang(url, city_name, city_code, my_coll, try_str=try_str)
        if not city_data:
            if self.shi_kuang_lose_total >= 3:
                self.log_to_file("ERROR：【实况】重试存储【{}】失败！".format(city_name))
                return False
            self.shi_kuang_lose_total += 1
            self.log_to_file("WARNING：【实况】存储【{}】失败！正在第{}次重试……".format(city_name, self.shi_kuang_lose_total))
            return self._get_shi_kuang(city_name, city_code, my_coll, try_str=True)

    def get_shi_kuang(self):
        my_db = self.client.shikuang
        for item in self.get_city():
            self.shi_kuang_lose_total = 0
            city_code = item['code']
            city_name = item['city']
            my_coll = my_db[city_code]
            self._get_shi_kuang(city_name=city_name, city_code=city_code, my_coll=my_coll, try_str=False)
            time.sleep(2)
        self.client.close()

    def test_mongo(self):
        my_db = self.client.yubao
        dblist = self.client.list_database_names()
        print(dblist)
        colls = my_db.list_collection_names()
        print(len(colls))
        self.client.close()

    def do(self):
        try:
            self.get_shi_kuang()
            time.sleep(5)
            self.get_yu_bao()
        except Exception as e:
            pass
            # self.log_to_file("{}".format(e))
            # params = ['天气错误']
            # self.sms(params)


if __name__ == '__main__':
    do = GetQiXiang()
    """直接执行代码"""
    # do.do()
    """定时执行代码"""
    hour = 23
    minute = 21
    scheduler = BlockingScheduler()
    scheduler.add_job(do.do, 'cron', hour=hour, minute=minute)
    print("定时成功：每天{}:{}开始执行！".format(hour, minute))
    scheduler.start()